home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / utilities / text / less-278.lha / less-278 / src.lha / source / line.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-01  |  12.2 KB  |  583 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Routines to manipulate the "line buffer".
  30.  * The line buffer holds a line of output as it is being built
  31.  * in preparation for output to the screen.
  32.  */
  33.  
  34. #include "less.h"
  35.  
  36. public char linebuf[1024];    /* Buffer which holds the current output line */
  37. public int size_linebuf = sizeof(linebuf);
  38.  
  39. static char attr[1024];        /* Extension of linebuf to hold attributes */
  40. static int curr;        /* Index into linebuf */
  41. static int column;        /* Printable length, accounting for
  42.                    backspaces, etc. */
  43. static int lno_indent;        /* Number of chars used for line number */
  44. static int overstrike;        /* Next char should overstrike previous char */
  45. static int is_null_line;    /* There is no current line */
  46. static char pendc;
  47. static POSITION pendpos;
  48.  
  49. static int do_append();
  50.  
  51. extern int bs_mode;
  52. extern int tabstop;
  53. extern int linenums;
  54. extern int ctldisp;
  55. extern int twiddle;
  56. extern int binattr;
  57. extern int auto_wrap, ignaw;
  58. extern int bo_s_width, bo_e_width;
  59. extern int ul_s_width, ul_e_width;
  60. extern int bl_s_width, bl_e_width;
  61. extern int so_s_width, so_e_width;
  62. extern int sc_width, sc_height;
  63.  
  64. /*
  65.  * Rewind the line buffer.
  66.  */
  67.     public void
  68. prewind()
  69. {
  70.     curr = 0;
  71.     column = 0;
  72.     overstrike = 0;
  73.     is_null_line = 0;
  74.     lno_indent = 0;
  75.     pendc = '\0';
  76. }
  77.  
  78. /*
  79.  * Insert the line number (of the given position) into the line buffer.
  80.  */
  81.     public void
  82. plinenum(pos)
  83.     POSITION pos;
  84. {
  85.     register int lno;
  86.     register int i;
  87.     register int n;
  88.  
  89.     /*
  90.      * We display the line number at the start of each line
  91.      * only if the -N option is set.
  92.      */
  93.     if (linenums != OPT_ONPLUS)
  94.         return;
  95.  
  96.     /*
  97.      * Get the line number and put it in the current line.
  98.      * {{ Note: since find_linenum calls forw_raw_line,
  99.      *    it may seek in the input file, requiring the caller 
  100.      *    of plinenum to re-seek if necessary. }}
  101.      */
  102.     lno = find_linenum(pos);
  103.  
  104.     sprintf(&linebuf[curr], "%6d", lno);
  105.     n = strlen(&linebuf[curr]);
  106.     column += n;
  107.     for (i = 0;  i < n;  i++)
  108.         attr[curr++] = 0;
  109.  
  110.     /*
  111.      * Append enough spaces to bring us to the next tab stop.
  112.      * {{ We could avoid this at the cost of adding some
  113.      *    complication to the tab stop logic in pappend(). }}
  114.      */
  115.     if (tabstop == 0)
  116.         tabstop = 1;
  117.     do
  118.     {
  119.         linebuf[curr] = ' ';
  120.         attr[curr++] = AT_NORMAL;
  121.         column++;
  122.     } while ((column % tabstop) != 0);
  123.     lno_indent = column;
  124. }
  125.  
  126. /*
  127.  * Return the printing width of the start (enter) sequence
  128.  * for a given character attribute.
  129.  */
  130.     int
  131. attr_swidth(a)
  132.     int a;
  133. {
  134.     switch (a)
  135.     {
  136.     case AT_BOLD:        return (bo_s_width);
  137.     case AT_UNDERLINE:    return (ul_s_width);
  138.     case AT_BLINK:        return (bl_s_width);
  139.     case AT_STANDOUT:    return (so_s_width);
  140.     }
  141.     return (0);
  142. }
  143.  
  144. /*
  145.  * Return the printing width of the end (exit) sequence
  146.  * for a given character attribute.
  147.  */
  148.     int
  149. attr_ewidth(a)
  150.     int a;
  151. {
  152.     switch (a)
  153.     {
  154.     case AT_BOLD:        return (bo_e_width);
  155.     case AT_UNDERLINE:    return (ul_e_width);
  156.     case AT_BLINK:        return (bl_e_width);
  157.     case AT_STANDOUT:    return (so_e_width);
  158.     }
  159.     return (0);
  160. }
  161.  
  162. /*
  163.  * Return the printing width of a given character and attribute,
  164.  * if the character were added to the current position in the line buffer.
  165.  * Adding a character with a given attribute may cause an enter or exit
  166.  * attribute sequence to be inserted, so this must be taken into account.
  167.  */
  168.     static int
  169. pwidth(c, a)
  170.     int c;
  171.     int a;
  172. {
  173.     register int w;
  174.  
  175.     if (c == '\b')
  176.         /*
  177.          * Backspace moves backwards one position.
  178.          */
  179.         return (-1);
  180.  
  181.     if (control_char(c))
  182.         /*
  183.          * Control characters do unpredicatable things,
  184.          * so we don't even try to guess; say it doesn't move.
  185.          * This can only happen if the -r flag is in effect.
  186.          */
  187.         return (0);
  188.  
  189.     /*
  190.      * Other characters take one space,
  191.      * plus the width of any attribute enter/exit sequence.
  192.      */
  193.     w = 1;
  194.     if (curr > 0 && attr[curr-1] != a)
  195.         w += attr_ewidth(attr[curr-1]);
  196.     if (a && (curr == 0 || attr[curr-1] != a))
  197.         w += attr_swidth(a);
  198.     return (w);
  199. }
  200.  
  201. /*
  202.  * Delete the previous character in the line buffer.
  203.  */
  204.     static void
  205. backc()
  206. {
  207.     curr--;
  208.     column -= pwidth(linebuf[curr], attr[curr]);
  209. }
  210.  
  211. /*
  212.  * Append a character and attribute to the line buffer.
  213.  */
  214.     static int
  215. storec(c, a, pos)
  216.     int c;
  217.     int a;
  218.     POSITION pos;
  219. {
  220.     register int w;
  221.  
  222. #if HILITE_SEARCH
  223.     if (is_hilited(pos, pos+1, 0))
  224.         /*
  225.          * This character should be highlighted.
  226.          * Override the attribute passed in.
  227.          */
  228.         a = AT_STANDOUT;
  229. #endif
  230.     w = pwidth(c, a);
  231.     if (ctldisp > 0 && column + w + attr_ewidth(a) > sc_width)
  232.         /*
  233.          * Won't fit on screen.
  234.          */
  235.         return (1);
  236.  
  237.     if (curr >= sizeof(linebuf)-2)
  238.         /*
  239.          * Won't fit in line buffer.
  240.          */
  241.         return (1);
  242.  
  243.     /*
  244.      * Special handling for "magic cookie" terminals.
  245.      * If an attribute enter/exit sequence has a printing width > 0,
  246.      * and the sequence is adjacent to a space, delete the space.
  247.      * We just mark the space as invisible, to avoid having too
  248.      * many spaces deleted.
  249.      * {{ Note that even if the attribute width is > 1, we
  250.      *    delete only one space.  It's not worth trying to do more.
  251.      *    It's hardly worth doing this much. }}
  252.      */
  253.     if (curr > 0 && a != AT_NORMAL && 
  254.         linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL &&
  255.         attr_swidth(a) > 0)
  256.     {
  257.         /*
  258.          * We are about to append an enter-attribute sequence
  259.          * just after a space.  Delete the space.
  260.          */
  261.         attr[curr-1] = AT_INVIS;
  262.         column--;
  263.     } else if (curr > 0 && attr[curr-1] != AT_NORMAL && 
  264.         attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL &&
  265.         attr_ewidth(attr[curr-1]) > 0)
  266.     {
  267.         /*
  268.          * We are about to append a space just after an 
  269.          * exit-attribute sequence.  Delete the space.
  270.          */
  271.         a = AT_INVIS;
  272.         column--;
  273.     }
  274.     /* End of magic cookie handling. */
  275.  
  276.     linebuf[curr] = c;
  277.     attr[curr] = a;
  278.     column += w;
  279.     return (0);
  280. }
  281.  
  282. /*
  283.  * Append a character to the line buffer.
  284.  * Expand tabs into spaces, handle underlining, boldfacing, etc.
  285.  * Returns 0 if ok, 1 if couldn't fit in buffer.
  286.  */
  287.     public int
  288. pappend(c, pos)
  289.     register int c;
  290.     POSITION pos;
  291. {
  292.     if (pendc)
  293.     {
  294.         if (do_append(pendc, pendpos))
  295.             /*
  296.              * Oops.  We've probably lost the char which
  297.              * was in pendc, since caller won't back up.
  298.              */
  299.             return (1);
  300.         pendc = '\0';
  301.     }
  302.  
  303.     if (c == '\r' && bs_mode == BS_SPECIAL)
  304.     {
  305.         /*
  306.          * Don't put the CR into the buffer until we see 
  307.          * the next char.  If the next char is a newline,
  308.          * discard the CR.
  309.          */
  310.         pendc = c;
  311.         pendpos = pos;
  312.         return (0);
  313.     }
  314.  
  315.     return (do_append(c, pos));
  316. }
  317.  
  318.     static int
  319. do_append(c, pos)
  320.     int c;
  321.     POSITION pos;
  322. {
  323.     register char *s;
  324.     register int a;
  325.  
  326. #define    STOREC(c,a) \
  327.     if (storec((c),(a),pos)) return (1); else curr++
  328.  
  329.     if (overstrike)
  330.     {
  331.         /*
  332.          * Overstrike the character at the current position
  333.          * in the line buffer.  This will cause either 
  334.          * underline (if a "_" is overstruck), 
  335.          * bold (if an identical character is overstruck),
  336.          * or just deletion of the character in the buffer.
  337.          */
  338.         overstrike = 0;
  339.         if ((char)c == linebuf[curr])
  340.             STOREC(linebuf[curr], AT_BOLD);
  341.         else if (c == '_')
  342.             STOREC(linebuf[curr], AT_UNDERLINE);
  343.         else if (linebuf[curr] == '_')
  344.             STOREC(c, AT_UNDERLINE);
  345.         else if (control_char(c))
  346.             goto do_control_char;
  347.         else
  348.             STOREC(c, AT_NORMAL);
  349.     } else if (c == '\b')
  350.     {
  351.         switch (bs_mode)
  352.         {
  353.         case BS_NORMAL:
  354.             STOREC(c, AT_NORMAL);
  355.             break;
  356.         case BS_CONTROL:
  357.             goto do_control_char;
  358.         case BS_SPECIAL:
  359.             if (curr == 0)
  360.                 break;
  361.             backc();
  362.             overstrike = 1;
  363.             break;
  364.         }
  365.     } else if (c == '\t') 
  366.     {
  367.         /*
  368.          * Expand a tab into spaces.
  369.          */
  370.         if (tabstop == 0)
  371.             tabstop = 1;
  372.         do
  373.         {
  374.             STOREC(' ', AT_NORMAL);
  375.         } while ((column % tabstop) != 0);
  376.     } else if (control_char(c))
  377.     {
  378.     do_control_char:
  379.         if (ctldisp == 0)
  380.         {
  381.             /*
  382.              * Output as a normal character.
  383.              */
  384.             STOREC(c, AT_NORMAL);
  385.         } else 
  386.         {
  387.             /*
  388.              * Convert to printable representation.
  389.              */
  390.             s = prchar(c);  
  391.             a = binattr;
  392.  
  393.             /*
  394.              * Make sure we can get the entire representation
  395.              * of the character on this line.
  396.              */
  397.             if (column + strlen(s) + 
  398.                 attr_swidth(a) + attr_ewidth(a) > sc_width)
  399.                 return (1);
  400.  
  401.             for ( ;  *s != 0;  s++)
  402.                 STOREC(*s, a);
  403.         }
  404.     } else
  405.     {
  406.         STOREC(c, AT_NORMAL);
  407.     }
  408.  
  409.     return (0);
  410. }
  411.  
  412. /*
  413.  * Terminate the line in the line buffer.
  414.  */
  415.     public void
  416. pdone(endline)
  417.     int endline;
  418. {
  419.     if (pendc && (pendc != '\r' || !endline))
  420.         /*
  421.          * If we had a pending character, put it in the buffer.
  422.          * But discard a pending CR if we are at end of line
  423.          * (that is, discard the CR in a CR/LF sequence).
  424.          */
  425.         (void) do_append(pendc, pendpos);
  426.  
  427.     /*
  428.      * Add a newline if necessary,
  429.      * and append a '\0' to the end of the line.
  430.      */
  431.     if (column < sc_width || !auto_wrap || ignaw || ctldisp == 0)
  432.     {
  433.         linebuf[curr] = '\n';
  434.         attr[curr] = AT_NORMAL;
  435.         curr++;
  436.     }
  437.     linebuf[curr] = '\0';
  438.     attr[curr] = AT_NORMAL;
  439. }
  440.  
  441. /*
  442.  * Get a character from the current line.
  443.  * Return the character as the function return value,
  444.  * and the character attribute in *ap.
  445.  */
  446.     public int
  447. gline(i, ap)
  448.     register int i;
  449.     register int *ap;
  450. {
  451.     char *s;
  452.     
  453.     if (is_null_line)
  454.     {
  455.         /*
  456.          * If there is no current line, we pretend the line is
  457.          * either "~" or "", depending on the "twiddle" flag.
  458.          */
  459.         *ap = AT_NORMAL;
  460.         s = (twiddle) ? "~\n" : "\n";
  461.         return (s[i]);
  462.     }
  463.  
  464.     *ap = attr[i];
  465.     return (linebuf[i] & 0377);
  466. }
  467.  
  468. /*
  469.  * Indicate that there is no current line.
  470.  */
  471.     public void
  472. null_line()
  473. {
  474.     is_null_line = 1;
  475. }
  476.  
  477. #if 1
  478. /*
  479.  * Analogous to forw_line(), but deals with "raw lines":
  480.  * lines which are not split for screen width.
  481.  * {{ This is supposed to be more efficient than forw_line(). }}
  482.  */
  483.     public POSITION
  484. forw_raw_line(curr_pos, linep)
  485.     POSITION curr_pos;
  486.     char **linep;
  487. {
  488.     register char *p;
  489.     register int c;
  490.     POSITION new_pos;
  491.  
  492.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  493.         (c = ch_forw_get()) == EOI)
  494.         return (NULL_POSITION);
  495.  
  496.     p = linebuf;
  497.  
  498.     for (;;)
  499.     {
  500.         if (c == '\n' || c == EOI)
  501.         {
  502.             new_pos = ch_tell();
  503.             break;
  504.         }
  505.         if (p >= &linebuf[sizeof(linebuf)-1])
  506.         {
  507.             /*
  508.              * Overflowed the input buffer.
  509.              * Pretend the line ended here.
  510.              * {{ The line buffer is supposed to be big
  511.              *    enough that this never happens. }}
  512.              */
  513.             new_pos = ch_tell() - 1;
  514.             break;
  515.         }
  516.         *p++ = c;
  517.         c = ch_forw_get();
  518.     }
  519.     *p = '\0';
  520.     if (linep != NULL)
  521.         *linep = linebuf;
  522.     return (new_pos);
  523. }
  524.  
  525. /*
  526.  * Analogous to back_line(), but deals with "raw lines".
  527.  * {{ This is supposed to be more efficient than back_line(). }}
  528.  */
  529.     public POSITION
  530. back_raw_line(curr_pos, linep)
  531.     POSITION curr_pos;
  532.     char **linep;
  533. {
  534.     register char *p;
  535.     register int c;
  536.     POSITION new_pos;
  537.  
  538.     if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() ||
  539.         ch_seek(curr_pos-1))
  540.         return (NULL_POSITION);
  541.  
  542.     p = &linebuf[sizeof(linebuf)];
  543.     *--p = '\0';
  544.  
  545.     for (;;)
  546.     {
  547.         c = ch_back_get();
  548.         if (c == '\n')
  549.         {
  550.             /*
  551.              * This is the newline ending the previous line.
  552.              * We have hit the beginning of the line.
  553.              */
  554.             new_pos = ch_tell() + 1;
  555.             break;
  556.         }
  557.         if (c == EOI)
  558.         {
  559.             /*
  560.              * We have hit the beginning of the file.
  561.              * This must be the first line in the file.
  562.              * This must, of course, be the beginning of the line.
  563.              */
  564.             new_pos = ch_zero();
  565.             break;
  566.         }
  567.         if (p <= linebuf)
  568.         {
  569.             /*
  570.              * Overflowed the input buffer.
  571.              * Pretend the line ended here.
  572.              */
  573.             new_pos = ch_tell() + 1;
  574.             break;
  575.         }
  576.         *--p = c;
  577.     }
  578.     if (linep != NULL)
  579.         *linep = p;
  580.     return (new_pos);
  581. }
  582. #endif
  583.